package com.david.mydemo.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.security.Key;
import java.util.Date;

/**
 * ClassName:TokenUtil
 */
@Slf4j
public class TokenUtil {


    /**
     * 生成访问Token
     *
     * @param data
     * @return 返回Token
     */
    public static String createAccessToken(Object data) {
        return createToken(data, MyConstants.ISSUER_ACCESS, MyConstants.EXPIRE_TIME);
    }

    /**
     * 生成刷新Token
     *
     * @param data
     * @return 返回Token
     */
    public static String createRefreshToken(Object data) {
        return createToken(data, MyConstants.ISSUER_REFRESH, MyConstants.LONG_EXPIRE_TIME);
    }

    /**
     * 更新访问Token
     * <p>同时把更新的token写入到cookie中</p>
     *
     * @param data
     */
    public static String updateAccessToken(Object data, HttpServletResponse response) {
        String newAccessToken = createAccessToken(data);
//        Cookie cookie = new Cookie(MyConstants.ACCESS_TOKEN_KEY, newAccessToken);
//        cookie.setPath(MyConstants.PATH);
//        response.addCookie(cookie);
        return newAccessToken;
    }

    /**
     * 更新刷新Token
     * <p>同时把更新的token写入到cookie中</p>
     *
     * @param data
     */
    public static String updateRefreshToken(Object data, HttpServletResponse response) {
        String newRefreshToken = createRefreshToken(data);
//        Cookie cookie = new Cookie(MyConstants.REFRESH_TOKEN_KEY, newRefreshToken);
//        cookie.setPath(MyConstants.PATH);
//        response.addCookie(cookie);
        return newRefreshToken;
    }

    private static String createToken(Object data, String issuer, long expireTime) {
        //生成签发时间，直接获取系统毫秒数
        long nowTime = System.currentTimeMillis();
        long expTime = nowTime + expireTime;
        //通过密钥签名JWT
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(MyConstants.TOKEN_SECRET);
        Key signingkey = new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName());
        //设置JWT声明
        JwtBuilder jwtBuilder = Jwts.builder()
                .setId(MyUtil.uuid())
                .setSubject(JSON.toJSONString(data))
                .signWith(SignatureAlgorithm.HS256, signingkey)
                .setIssuer(issuer)
                .setIssuedAt(new Date(nowTime))
                .setNotBefore(new Date(nowTime))
                .setExpiration(new Date(expTime));
        ;
        //构建JWT并将其序列化为一个紧凑的url安全字符串
        return jwtBuilder.compact();
    }

    /**
     * Token解析方法
     *
     * @param token
     */
    public static Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(DatatypeConverter.parseBase64Binary(MyConstants.TOKEN_SECRET))
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * token 解密，并返回一个对象，可是User对象
     *
     * @param token
     * @param clazz
     * @return
     */
    public static <T> T parseToken(String token, String issuer, Class<T> clazz) {
        try {
            Claims body = Jwts.parser()
                    .setSigningKey(DatatypeConverter.parseBase64Binary(MyConstants.TOKEN_SECRET))
                    .parseClaimsJws(token)
                    .getBody();
            if (!StringUtils.equals(body.getIssuer(), issuer)) {
                log.error("用户token解析对象失败，签发人不匹配！token：[" + token + "]");
                return null;
            }
            return JSON.parseObject(body.getSubject(), clazz);
        } catch (Exception e) {
            log.error("用户token解析对象失败！token：[" + token + "]", e);
            return null;
        }
    }

    /**
     * Token验证
     * <p>true：验证成功</p>
     */
    public static boolean checkToken(String token, String issuer) {
//        if (StringUtils.isEmpty(token)) {
//            log.error("用户token验证失败，token为空！");
//            return false;
//        }
        try {
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(MyConstants.TOKEN_SECRET)).parseClaimsJws(token);
            if (!StringUtils.equals(claimsJws.getBody().getIssuer(), issuer)) {
                log.error("用户token验证失败，签发人不匹配，禁止访问！token：[" + token + "]");
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error("用户token验证失败，禁止访问！token：[" + token + "]", e);
            return false;
        }
    }

    /**
     * 获取访问token参数
     */
    public static String getAccessTokenParam(HttpServletRequest request) {
//        Cookie[] cookies = request.getCookies();
//        for (Cookie cookie : cookies) {
////            if (MyConstants.ACCESS_TOKEN_KEY.equals(cookie.getName()) && MyConstants.PATH.equals(cookie.getPath())) {
//            if (MyConstants.ACCESS_TOKEN_KEY.equals(cookie.getName())) {
//                log.info("从Cookies中读取token信息，cookie.value：{}", cookie.getValue());
//                return cookie.getValue();
//            }
//        }
        return request.getHeader(MyConstants.ACCESS_TOKEN_KEY);
    }

    /**
     * 获取刷新token参数
     */
    public static String getRefreshTokenParam(HttpServletRequest request) {
//        Cookie[] cookies = request.getCookies();
//        for (Cookie cookie : cookies) {
////            if (MyConstants.REFRESH_TOKEN_KEY.equals(cookie.getName()) && MyConstants.PATH.equals(cookie.getPath())) {
//            if (MyConstants.REFRESH_TOKEN_KEY.equals(cookie.getName())) {
//                log.info("从Cookies中读取token信息，cookie.value：{}", cookie.getValue());
//                return cookie.getValue();
//            }
//        }
        return request.getHeader(MyConstants.REFRESH_TOKEN_KEY);
    }

    /**
     * 响应异常信息
     */
    public static void responseFail(HttpServletResponse response, String message) throws IOException {
        MyResponse MyResponse = new MyResponse().fail(message);
        MyResponse.code(443); // token校验失败自定义code
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(JSONObject.toJSONString(MyResponse, SerializerFeature.WriteMapNullValue));
    }

    /**
     * 获取用户
     */
    public static LoginUserDto getUser(HttpServletRequest request) {
        LoginUserDto loginUserDto = (LoginUserDto) request.getAttribute(MyConstants.ACCESS_TOKEN_USER_KEY);
        return loginUserDto;
    }

}
